package com.anxin.act.task.service;

import com.alibaba.fastjson.JSONArray;
import com.anxin.act.task.addTask.ChainedActivitiesCreator;
import com.anxin.act.task.addTask.MultiInstanceActivityCreator;
import com.anxin.act.task.addTask.RuntimeActivityDefinitionEntityIntepreter;
import com.anxin.act.task.addTask.SimpleRuntimeActivityDefinitionEntity;
import com.anxin.act.task.cmd.CreateAndTakeTransitionCmd;
import com.anxin.act.task.cmd.SequentialAndParallelCountersignAddcmd;
import com.anxin.act.task.dao.ActTaskDao;
import com.anxin.act.task.dao.WfActHiTaskDao;
import com.anxin.act.task.dao.WfActRuTaskDao;
import com.anxin.act.task.dao.WfActivityCreationDao;
import com.anxin.act.task.entity.ViewRuTask;
import com.anxin.act.task.entity.WfActRuTask;
import com.anxin.act.task.vo.TaskVo;
import com.anxin.act.utils.ActTaskUtils;
import com.anxin.act.utils.ActUtils;
import com.anxin.common.utils.IdGen;
import com.anxin.common.utils.sys.UserUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.RuntimeServiceImpl;
import org.activiti.engine.impl.interceptor.CommandExecutor;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 流程加签
 *
 * @author: liuxiangyu
 * @date: 2020/5/12 14:39
 */
@Service
public class AddTaskService {
    private static Logger logger = LoggerFactory.getLogger("sys-error");

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private JumpTaskService jumpTaskService;

    @Autowired
    private WfActivityCreationDao wfActivityCreationDao;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ActTaskDao actTaskDao;

    @Autowired
    private WfActRuTaskDao wfActRuTaskDao;

    @Autowired
    private WfActHiTaskDao wfActHiTaskDao;

    /**
     * 挂起Activiti任务
     * @param viewRuTask 待办视图对象
     */
    public void hangTask(ViewRuTask viewRuTask){

        if ("actTask".equals(viewRuTask.getType())){
            Task task = taskService.createTaskQuery().taskId(viewRuTask.getId()).singleResult();
            // 审批人设置为-2
            taskService.setAssignee(task.getId(),"-2");
            // 讲原本的审批人这只到流程变量中
            if (!"-2".equals(task.getAssignee())){
                runtimeService.setVariableLocal(task.getExecutionId(),"actPreviousApprover",task.getAssignee());
            }
        }else {
            wfActRuTaskDao.hangTask(viewRuTask.getId());
            wfActHiTaskDao.hangTask(viewRuTask.getId());
        }


    }

    /**
     * 恢复任务
     * @param taskId 待办ID
     */
    public void restoreTask(String taskId){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 查询出之前的审批人
        if (task != null){
            Object actPreviousApprover = runtimeService.getVariable(task.getExecutionId(), "actPreviousApprover");
            // 将审批人还原
            if (actPreviousApprover != null){
                taskService.setAssignee(taskId,actPreviousApprover.toString());
            }
        }
    }



    /**
     * 后加签单个节点
     *
     * @param taskVo
     * @param addType   加签类型
     * @param variables 需要鞋带的变量
     * @param assignees 加签节点的审批人
     * @return
     */
    public ActivityImpl[] insertTasksAfter(TaskVo taskVo, String addType, Map<String, Object> variables, String assignees) {

        ActivityImpl prototypeActivity = ActUtils.getActivityImpl(taskVo.getProcessDefId(), taskVo.getTaskKey());
        String nextActivityId = prototypeActivity.getOutgoingTransitions().get(0).getDestination().getId();
        return cloneAndMakeChain(taskVo, addType, nextActivityId, variables, assignees);
    }


    /**
     * 创建节点
     *
     * @param taskVo 待办提交对象
     * @param addType 提交类型
     * @param nextActivityId 新节点的下一个节点
     * @param variables 流程变量
     * @param assignees 新节点的审批人
     * @return 创建后的对象
     */
    public ActivityImpl[] cloneAndMakeChain(TaskVo taskVo, String addType, String nextActivityId, Map<String, Object> variables, String... assignees) {
        SimpleRuntimeActivityDefinitionEntity info = new SimpleRuntimeActivityDefinitionEntity();
        info.setProcessDefinitionId(taskVo.getProcessDefId());
        info.setProcessInstanceId(taskVo.getActId());

        RuntimeActivityDefinitionEntityIntepreter radei = new RuntimeActivityDefinitionEntityIntepreter(info);
        radei.setPrototypeActivityId(taskVo.getTaskKey());
        radei.setAssignees((List<String>) CollectionUtils.arrayToList(assignees));
        radei.setNextActivityId(nextActivityId);

        //获取流程定义
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(taskVo.getProcessDefId());
        ActivityImpl[] activities = new ChainedActivitiesCreator().createActivities(processEngine, processDefinition, info, addType);

        //跳转到新创建的节点
        jumpTaskService.jumpTask(taskVo.getActId(), taskVo.getTaskId(), activities[0].getId(), variables);

        try {
            info.serializeProperties();

            for (Object cloneActivityId : info.getCloneActivityIds().toArray()) {
                info.setCloneActivityId(cloneActivityId.toString());
                info.setId(IdGen.uuid());
                wfActivityCreationDao.insert(info);
            }

        } catch (JsonProcessingException e) {
            logger.error("创建节点异常", e);
        }
        return activities;
    }

    /**
     * 后加签 一个人
     * @param taskVo 提交对象
     */
    public ActivityImpl[] addTaskAfter(TaskVo taskVo){
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(taskVo.getProcessDefId());

        // 当前节点的activity对象
        ActivityImpl activityImpl = ActUtils.getActivityImpl(processDefinition.getId(), taskVo.getTaskKey());

        String nextActivityId = activityImpl.getOutgoingTransitions().get(0).getDestination().getId();
        return this.cloneAndMakeChain(taskVo,"actAfter",nextActivityId,taskVo.getOpinions(), UserUtils.idToLoginName(taskVo.getAddUser()).getString(0));
    }

    /**
     * 后加签并行节点
     * @param taskVo
     * @return
     */
    public ActivityImpl addParallelTaskAfter(TaskVo taskVo){
        JSONArray addUserArray = UserUtils.idToLoginName(taskVo.getAddUser());
        ActivityImpl[] actBefore = this.insertTasksAfter(taskVo, "actAfter",taskVo.getOpinions(),  addUserArray.getString(0));


        TaskEntity addTask = (TaskEntity) ActTaskUtils.getTask(actBefore[0].getId(), taskVo.getActId());


        // 分裂节点
        ActivityImpl activity = this.splitTask(addTask.getProcessDefinitionId(), addTask.getProcessInstanceId(), addTask, new HashMap<String, Object>(), false, "actAfter", addUserArray.toArray(new String[0]));

        List<Task> taskList = ActTaskUtils.getTaskList(actBefore[0].getId(), taskVo.getActId());


        for (int i = 0; i < taskList.size(); i++) {
            Task task = taskList.get(i);
            taskService.setAssignee(task.getId(), addUserArray.getString(i));
            actTaskDao.updateHiActInstAssignee(task.getId(), addUserArray.getString(i));
            this.updateTaskName("actAfter",task.getId());
        }

        actTaskDao.delHiActInst(addTask.getId());
        actTaskDao.delHiTaskInst(addTask.getId());

        return activity;
    }


    /**
     * 前加签
     * @param taskVo
     * @return
     */
    public ActivityImpl[] addTaskBefore(TaskVo taskVo) {
        ProcessDefinitionEntity procDef = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(taskVo.getProcessDefId());
        return cloneAndMakeChain(taskVo,"actBefore",taskVo.getTaskKey(),taskVo.getOpinions(),UserUtils.idToLoginName(taskVo.getAddUser()).getString(0));
    }

    /**
     * 前加签
     * @param taskVo
     * @param userName
     * @return
     */
    public ActivityImpl[] addTaskBefore(TaskVo taskVo,String userName) {
        return cloneAndMakeChain(taskVo,"actBefore",taskVo.getTaskKey(),taskVo.getOpinions(),userName);
    }

    /**
     * 分裂某节点为多实例节点
     */
    @SuppressWarnings("unchecked")
    public ActivityImpl splitTask(String procDefId, String procInsId, TaskEntity taskEntity, Map<String, Object> variables, boolean isSequential, String type, String... assignees) {
        SimpleRuntimeActivityDefinitionEntity info = new SimpleRuntimeActivityDefinitionEntity();
        info.setProcessDefinitionId(procDefId);
        info.setProcessInstanceId(procInsId);

        RuntimeActivityDefinitionEntityIntepreter radei = new RuntimeActivityDefinitionEntityIntepreter(info);

        radei.setPrototypeActivityId(taskEntity.getTaskDefinitionKey());
        radei.setAssignees((List<String>) CollectionUtils.arrayToList(assignees));
        radei.setSequential(isSequential);

        //获取到当前的流程定义
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(procDefId);

        //创建多实例节点
        ActivityImpl clone = new MultiInstanceActivityCreator().createActivities(processEngine, processDefinition, info,type)[0];
        CommandExecutor commandExecutor = ((RuntimeServiceImpl) runtimeService).getCommandExecutor();
        commandExecutor.execute(new CreateAndTakeTransitionCmd(taskEntity, clone, variables));

        try {

            info.serializeProperties();
            info.setCloneActivityId(clone.getId());
            info.setId(IdGen.uuid());
            wfActivityCreationDao.insert(info);

        } catch (JsonProcessingException e) {
            logger.error("分裂某节点为多实例节点异常", e);
        }
        return clone;
    }

    /**
     * 并行节点加签
     * @param taskVo
     * @param addType 加签类型
     *                actBefore:前加签
     *                actAfter:后加签
     * @return
     */
    public void addParallelBefore(TaskVo taskVo,String addType) {
        CommandExecutor commandExecutor = ((RuntimeServiceImpl) runtimeService).getCommandExecutor();
        JSONArray assigneeNameList = UserUtils.idToLoginName(taskVo.getAddUser());

        String actBeforeUuid = IdGen.uuid();

        // 是否为前加签
        boolean isBefore = "actBefore".endsWith(addType);

        for (int i = 0; i < assigneeNameList.size(); i++) {
            String execute = commandExecutor.execute(new SequentialAndParallelCountersignAddcmd(taskVo.getTaskId(), assigneeNameList.getString(i), runtimeService, taskService, isBefore));
            Task addTask = taskService.createTaskQuery().executionId(execute).singleResult();
            if (isBefore){
                // 会签节点前加签时，执行前加签操作的节点的taskId
                taskService.setVariableLocal(addTask.getId(),"actSourceTaskId",taskVo.getTaskId());
                // 会签节点加签时生成，同一次加签出的多个节点这个值是相同的
                taskService.setVariableLocal(addTask.getId(),"actBeforeUuid",actBeforeUuid);
            }
            // 修改节点名称，增加(前加签) or (后加签)
            this.updateTaskName(addType,addTask.getId());
        }

        taskService.complete(taskVo.getTaskId(), taskVo.getOpinions());
    }

    /**
     * 修改加签节点名称
     * @param addType 加签类型
     *                actBefore:(前加签)
     *                actAfter:(后加签)
     *                actEntrustment:(转签)
     *                reading:阅办人
     * @param taskId 待办任务ID
     */
    public void updateTaskName(String addType,String taskId){
        String name = "";

        switch (addType){
            case "actBefore": name = "(前加签)"; break;
            case "actAfter": name = "(后加签)"; break;
            case "actEntrustment": name = "(转签)"; break;
            case "reading": name = "(阅办人)"; break;
        }

        actTaskDao.updateHiTaskName(taskId,name);
        actTaskDao.updateRuTaskName(taskId,name);
    }

    /**
     * 修改加签节点名称
     * @param addType 加签类型
     *                actBefore:(前加签)
     *                actAfter:(后加签)
     *                actEntrustment:(转签)
     *                reading:阅办人
     * @param ActivityImplId ActivityId
     * @param actId 流程实例ID
     */
    public void updateTaskNameByActivityImplId(String addType,String ActivityImplId,String actId){
        List<Task> taskList = ActTaskUtils.getTaskList(ActivityImplId, actId);
        for (Task task : taskList) {
            // 修改节点名称，增加(前加签)
            this.updateTaskName(addType,task.getId());
        }

    }
}
